package com.zhijian.medical.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.zhijian.BusinessException;
import com.zhijian.medical.dao.local.RoutinePathologyQpMapper;
import com.zhijian.medical.entity.dto.PersonDto;
import com.zhijian.medical.entity.po.*;
import com.zhijian.medical.entity.vo.request.*;
import com.zhijian.medical.entity.vo.response.*;
import com.zhijian.medical.enums.RoutineHistoryEnum;
import com.zhijian.medical.enums.RoutinePathologySampleStepEnum;
import com.zhijian.medical.holder.CurrentUserHolder;
import com.zhijian.medical.service.*;
import com.zhijian.util.AssertUtil;
import com.zhijian.util.page.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 常规病例切片业务处理层
 *
 * @author auto
 * @version: 1.0
 * @date 2024-06-23 15:24:06
 */
@Slf4j
@Service
public class RoutinePathologyQpServiceImpl implements RoutinePathologyQpService {

    @Autowired
    private RoutinePathologyQpMapper mapper;

    @Autowired
    private RoutinePathologySampleService routinePathologySampleService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private RoutineHistoryService historyService;


    @Autowired
    private RoutineStepService stepService;

    @Autowired
    private RoutineStepRecordService stepRecordService;

    @Autowired
    private RoutineSingleSampleService singleSampleService;

    @Autowired
    private RoutineQcService qcService;

    @Autowired
    private DeviceAdjustRecordService adjustRecordService;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private RoutineSingleSampleQpService routineSingleSampleQpService;
    @Autowired
    private SpecialInstructionService specialInstructionService;
    @Autowired
    private TechInstructionService techInstructionService;

    @Override
    public RoutinePathologyQp selectById(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public RoutinePathologyQpVo selectVoById(Long id) {
        return mapper.selectVoById(id);
    }

    @Override
    public void deleteById(Long id) {
        mapper.deleteByPrimaryKey(id);
    }

    @Override
    public void deleteByIdList(List<Long> idList) {
        if (CollectionUtil.isEmpty(idList)) {
            return;
        }
        mapper.deleteByIdList(idList);
    }

    @Override
    public Long insert(RoutinePathologyQp routinePathologyQp) {
        Date now = new Date();
        routinePathologyQp.setCreateTime(now);
        routinePathologyQp.setUpdateTime(now);
        return mapper.insert(routinePathologyQp) > 0 ? routinePathologyQp.getId() : 0L;
    }

    @Override
    public Long insertSelective(RoutinePathologyQp routinePathologyQp) {
        Date now = new Date();
        routinePathologyQp.setCreateTime(now);
        routinePathologyQp.setUpdateTime(now);
        return mapper.insertSelective(routinePathologyQp) > 0 ? routinePathologyQp.getId() : 0L;
    }

    @Override
    public boolean update(RoutinePathologyQp routinePathologyQp) {
        Date now = new Date();
        routinePathologyQp.setUpdateTime(now);
        return mapper.updateByPrimaryKey(routinePathologyQp) != 0;
    }

    @Override
    public boolean updateByPrimaryKeySelective(RoutinePathologyQp record) {
        Date now = new Date();
        record.setUpdateTime(now);
        return mapper.updateByPrimaryKeySelective(record) != 0;
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public boolean save(RoutinePathologyQpSaveOrUpdateVo saveOrUpdateVo) {
        RoutinePathologySample routinePathologySample = routinePathologySampleService.selectById(saveOrUpdateVo.getSampleId());
        AssertUtil.notNull(routinePathologySample, "此样本不存在或者已被删除");
        AssertUtil.check(routinePathologySample.getStep() != RoutinePathologySampleStepEnum.BM.getCode().byteValue(), "此样本不允许切片");
        Integer count = this.count(RoutinePathologyQpSearchVo.builder()
                .sampleId(saveOrUpdateVo.getSampleId())
                .build());
        AssertUtil.check(count > 0, "此样本不允许切片");
        RoutinePathologyQp routinePathologyQp = new RoutinePathologyQp();
        BeanUtils.copyProperties(saveOrUpdateVo, routinePathologyQp);
        //把样本改成染色状态
        routinePathologySampleService.updateByPrimaryKeySelective(RoutinePathologySample.builder()
                .id(saveOrUpdateVo.getSampleId())
                .step(RoutinePathologySampleStepEnum.RS.getCode().byteValue())
                .build());
        return this.insert(routinePathologyQp) != 0;
    }

    @Override
    public boolean modify(RoutinePathologyQpSaveOrUpdateVo saveOrUpdateVo) {
        RoutinePathologyQp old = this.selectById(saveOrUpdateVo.getId());
        AssertUtil.notNull(old, "此数据不存在或者已被删除");
        RoutinePathologyQp routinePathologyQp = new RoutinePathologyQp();
        BeanUtils.copyProperties(saveOrUpdateVo, routinePathologyQp);
        return this.update(routinePathologyQp);
    }

    @Override
    public RoutinePathologyQpVo selectOne(RoutinePathologyQpSearchVo routinePathologyQpSearchVo) {
        return mapper.selectOne(routinePathologyQpSearchVo);
    }

    @Override
    public Integer count(RoutinePathologyQpSearchVo routinePathologyQpSearchVo) {
        return mapper.count(routinePathologyQpSearchVo);
    }

    @Override
    public List<RoutinePathologyQpVo> selectList(RoutinePathologyQpSearchVo routinePathologyQpSearchVo) {
        return mapper.selectList(routinePathologyQpSearchVo);
    }

    @Override
    public Page<RoutinePathologyQpVo> selectPage(RoutinePathologyQpSearchVo routinePathologyQpSearchVo) {
        AssertUtil.check(Objects.isNull(routinePathologyQpSearchVo.getPageNum()) || Objects.isNull(routinePathologyQpSearchVo.getPageSize()), "分页参数不能为空");
        //先直接查总数
        int count = count(routinePathologyQpSearchVo);
        //分页
        Page<RoutinePathologyQpVo> page = new Page<>(true, count, routinePathologyQpSearchVo.getPageNum(), routinePathologyQpSearchVo.getPageSize());
        routinePathologyQpSearchVo.setStartIndex(page.getStartIndex());
        page.setPage(true);
        page.setTotalCount(count);
        List<RoutinePathologyQpVo> responseVos = this.selectList(routinePathologyQpSearchVo);
        page.setData(responseVos);
        return page;
    }

    @Override
    public boolean insertBatch(List<RoutinePathologyQp> entities) {
        return CollectionUtil.isNotEmpty(entities) && mapper.insertBatch(entities) > 0;
    }

    @Override
    public void deleteByCondition(RoutinePathologyQpSearchVo routinePathologyQpSearchVo) {
        //慎重用此方法！！
        mapper.deleteByCondition(routinePathologyQpSearchVo);
    }

    private String generateQpKey(PersonDto personDto, Integer type){
        if (type != null && type > 0){
            return "gl_qiepian:"+ type +":" + personDto.getId();
        }else {
            return "gl_qiepian:" + personDto.getId();
        }
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public RoutinePathologyBmInfoVo pathologyInfo(String numbers, Integer type) {
        PersonDto personDto = CurrentUserHolder.getUser();
        //切片key
        String qpKey = generateQpKey(personDto, type);
        // 找到病理号
        String[] split = numbers.split("-");
        com.zhijian.medical.util.AssertUtil.check(split.length != 2, "非法的蜡块号");
        String bingLiHao = split[0];

        // 深切、连切、间断连切、重切对应的蜡块号可以在常规流程的切片功能中再次进行切片；
        List<TechInstructionVo> instructionVos = techInstructionService.selectList(TechInstructionSearchVo.builder().waxBlock(numbers).build());
        boolean needJudgeFlow = true;
        for (TechInstructionVo techInstructionVo : instructionVos) {
            if (techInstructionVo.getType().compareTo((byte)5) < 0) {
                needJudgeFlow = false;
                break;
            }
        }
        if (needJudgeFlow) {
            // 是否已录入
            String infoStr = cacheService.get(qpKey);
            if (StringUtils.isNotBlank(infoStr)) {
                com.zhijian.medical.util.AssertUtil.check(infoStr.contains(numbers), "信息已录入");
            }
        }

        // 校验流程是否合法--暂时关闭
//        Integer hisCount = historyService.count(RoutineHistorySearchVo.builder()
//                .step(RoutineHistoryEnum.QP.getCode().byteValue())
//                .number(numbers)
//                .build());
//        com.zhijian.medical.util.AssertUtil.check(hisCount > 0, "流程不合法");

        RoutineStepVo routineStepVo = stepService.selectOne(RoutineStepSearchVo.builder().conventionPathologyNumber(bingLiHao).build());

        RoutinePathologyBmInfoVo infoVo = new RoutinePathologyBmInfoVo();
        // 设备状态
        List<DeviceTsVo> deviceVos = new ArrayList<>();
        List<Device> devices = deviceService.selectListByType("切片");
        for (Device device : devices) {
            DeviceTsVo deviceTsVo = new DeviceTsVo();
            BeanUtils.copyProperties(device, deviceTsVo);
            // 默认维修时间是现在时间- 3 个月
            Date adjustDate = DateUtil.offset(new Date(), DateField.MONTH, -3);
            String adjustTime = DateUtil.formatDateTime(adjustDate);
            // 默认下次维修时间是现在时间+8个月
            adjustDate = DateUtil.offset(new Date(), DateField.MONTH, 8);
            String nextAdjustTime = DateUtil.formatDateTime(adjustDate);
            // 查找维修记录
            List<DeviceAdjustRecordVo> deviceAdjustRecordVos = adjustRecordService.selectList(DeviceAdjustRecordSearchVo.builder().deviceId(deviceTsVo.getId()).build());
            // 有记录
            if (CollUtil.isNotEmpty(deviceAdjustRecordVos)) {
                //adjustTime = deviceAdjustRecordVos.get(0).getCreateTime();
                adjustTime = deviceAdjustRecordVos.get(0).getDate() + " 00:00:00";
                adjustDate = DateUtil.parse(adjustTime);
                nextAdjustTime = DateUtil.formatDateTime(DateUtil.offset(adjustDate, DateField.MONTH, 11));
            }
            deviceTsVo.setJzrq(adjustTime);
            deviceTsVo.setXcjzrq(nextAdjustTime);
            deviceVos.add(deviceTsVo);
        }
        infoVo.setDeviceVos(deviceVos);
        if (StringUtils.isBlank(numbers)) {
            return infoVo;
        }
        //检查白切的时候是否设置切片数
        RoutineSingleSampleQpService.QpTypeEnum[] qpTypeEnums = RoutineSingleSampleQpService.QpTypeEnum.values();
        if (type != null && type > 0){
            RoutineSingleSampleQpService.QpTypeEnum qpTypeEnum = qpTypeEnums[type];
            if (qpTypeEnum.equals(RoutineSingleSampleQpService.QpTypeEnum.WHITE)){
                String numStr = cacheService.get("gl_qiepian_num:" + personDto.getId());
                com.zhijian.medical.util.AssertUtil.check(StringUtils.isBlank(numStr), "白片需要设置切片数量");
            }
        }
        // redis记录蜡块号
        String inputNums = cacheService.get(qpKey);
        if (StringUtils.isNotBlank(inputNums)) {
            cacheService.setex(qpKey, 5*60,inputNums + "," + numbers);
        } else {
            cacheService.setex(qpKey, 5*60, numbers);
        }
        // 生成切片
        if (type != null && type > 0){
            RoutineSingleSampleQpService.QpTypeEnum qpTypeEnum = qpTypeEnums[type];
            if (qpTypeEnum.equals(RoutineSingleSampleQpService.QpTypeEnum.WHITE)){
                String numStr = cacheService.get("gl_qiepian_num:" + personDto.getId());
                com.zhijian.medical.util.AssertUtil.check(StringUtils.isBlank(numStr), "白片需要设置切片数量");
                Integer moreNum = Integer.parseInt(numStr);
                routineSingleSampleQpService.generateQp(personDto, moreNum, qpTypeEnum);
            }else {
                routineSingleSampleQpService.generateQp(personDto, qpTypeEnum);
            }
        }else {
            routineSingleSampleQpService.generateQp(personDto, RoutineSingleSampleQpService.QpTypeEnum.HE);
        }
        // 增加切片记录
        historyService.insert(RoutineHistory.builder()
                .conventionPathologyNumber(bingLiHao)
                .step(RoutineHistoryEnum.QP.getCode().byteValue())
                .number(numbers)
                .build());
        // 增加病理号流程记录
        String contetnt = "在实验室切片";
        if (Objects.nonNull(routineStepVo) && Objects.nonNull(routineStepVo.getStep()) && routineStepVo.getStep() < RoutinePathologySampleStepEnum.QP.getCode().byteValue()) {
            stepService.updateStepByNums(bingLiHao, contetnt, RoutinePathologySampleStepEnum.QP.getCode().byteValue());
            stepRecordService.insert(RoutineStepRecord.builder()
                    .conventionPathologyNumber(bingLiHao)
                    .step(RoutinePathologySampleStepEnum.QP.getCode().byteValue())
                    .content(contetnt)
                    .build());
        }
        // 返回蜡块号相关信息：取材信息、肉眼状态、样本状态、设备信息
        // 取材信息
        RoutineSingleSampleVo singleSampleVo = singleSampleService.selectOne(RoutineSingleSampleSearchVo.builder().number(numbers).build());
        BeanUtils.copyProperties(singleSampleVo, infoVo);
        // 肉眼状态
        RoutineQcVo routineQcVo = qcService.selectOne(RoutineQcSearchVo.builder().conventionPathologyNumber(bingLiHao).build());
        infoVo.setRysj(routineQcVo.getRySj());
        // 状态
        routineStepVo.setStep(RoutinePathologySampleStepEnum.BM.getCode().byteValue());
        routineStepVo.setCreateTime(DateUtil.formatDateTime(new Date()));
        routineStepVo.setContent(contetnt);
        routineStepVo.setConventionPathologyNumber(bingLiHao);
        infoVo.setStepVo(routineStepVo);

        infoVo.setConventionPathologyNumber(bingLiHao);
        // 特殊医嘱  1-免疫组化；2-特殊染色；3-分子检测；4-免疫荧光；5-试行项目
        byte specialType ;
        if (type.equals(3)){
            specialType = (byte) 2;
        } else if (type.equals(2)) {
            specialType = (byte) 1;
        } else {
            specialType = 0;
        }
        List<SpecialInstructionVo> specialInstructionVos = specialInstructionService.selectList(SpecialInstructionSearchVo.builder()
                .waxBlock(numbers)
                .type(specialType)
                .build());
        infoVo.setSpecialInstructionVos(specialInstructionVos);
        return infoVo;
    }
}